/******************************************************************************

    Copyright (C) 2006 Alessio Stalla (alessiostalla@gmail.com)

    This file is part of PrettyProlog.

    PrettyProlog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    PrettyProlog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrettyProlog; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    See the file COPYING for further details.

 *****************************************************************************/

package prettyprolog.datatypes;

import java.util.Vector;

import prettyprolog.engine.Domain;

/**
 * A Variable X is a Term which can be unified with any other Term T, provided
 * that T doesn't contain X.
 */
public class Variable extends AbstractTerm {
	
	private static final long serialVersionUID = 1L;

	/** The Symbol that represents this Variable's name. */
	private Symbol sym;
    public Vector dom;
    public int value;
    public Variable var_orig; 		// variabile originale dichiarata
    public Vector MinMax;	
	/**
	 * Whether this Variable is temporary, i.e. generated by the engine while
	 * solving.
	 */
	private boolean temporary = false;
	/** Whether this Variable has a name. */
	private boolean anon = false;

	/** Constructs a new Variable with name <code>s</code>. */
	public Variable(Symbol s) {
		sym = s;
		var_orig = null;
        dom = new Vector();
        value = 0;		
		if (s.getName().equals("_")) {
			anon = true;
		} else if (s.getName().charAt(0) == '_') {
			temporary = true;
		}
		new Variable (sym, dom, value, var_orig);
	}
	
    public Variable (Symbol s, Vector v, int val, Variable var) {
        sym = s;
        dom = v;
        value = val;
        var_orig = var;
    }	
    
    public Symbol getVariable() {
        return this.sym;
    }

    public Variable getSubst() {
    	return this.var_orig;
    }
    
    public void setDom (Vector v) {
    	this.dom = v;
    }
    
    public void setSubst (Term t) {
    	if (!((t instanceof Real) || (t instanceof Constant) || (t instanceof Cons))) {
    		this.var_orig = (Variable) t;
    	}
    }
    
    public Vector getDomain () {
        return this.dom;
    }
    
    public int min() {
    	return (Integer) this.dom.get(0);
    }
    
    public int max() {
    	return (Integer) this.dom.get(this.dom.size()-1);
    }
    
    public void deleteDomain() {
    	this.dom.removeAllElements();
    }

    public int getValue() {
        return this.value;
    }

    public void setValue (int val) {
        this.value = val;
    }    
 
    public boolean domainEmpty() {
        return dom.isEmpty();
    }    

	public Term apply(Substitution subst) {
		Term t = subst.getValue(this);
		if (t != null)
			return t;
		else
			return this;
	}
	
	public void applySubst(Substitution subst) {
		Variable t = (Variable) subst.getValue(this);
		this.sym = t.sym;
	}

	public boolean isGround(boolean strict) {
		if (strict)
			return false;
		else
			return (anon || temporary);
	}

	/**
	 * A Variable is temporary if it is automatically generated, for examples
	 * when renaming a Clause's Variables, and thus it mustn't be shown to the
	 * user.
	 */
	public void setTemporary(boolean b) {
		temporary = b;
	}

	/**
	 * @return true if this Variable has been set temporary, false otherwise.
	 */
	public boolean isTemporary() {
		return temporary;
	}

	/** @return true if this is the special anonymous Variable, i.e. _ */
	public boolean isAnonymous() {
		return anon;
	}

	public Term copy(Substitution vars) {
		if (isAnonymous()) {
			return this;
		}
		Variable var = (Variable) vars.getValue(this);
		Vector dom = Domain.getDomain(this);
		if (var == null) {
			var = new Variable(Namespace.gensym());
			var.setTemporary(true);
			Domain.insertDomain(var);
			var.setSubst(this);
			var.dom = this.dom;
			Domain.updateDomain(var, this.dom);
			vars.putMapping(this, var);
		}
		return var;
	}

	public String toString() {
		return sym.toString();
	}

	public int hashCode() {
		return sym.hashCode();
	}

	public boolean equals(Object o) {
		return ((o instanceof Variable) && (((Variable) o).sym.equals(sym)));
	}
}